#include "main_window.h"
#include "ui_mainwindow.h"

#include "math_utils.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->objects_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);


    m_scene = new Scene();   
    ui->top_right_viewport_frame->hide();
    ui->bottom_viewport_frames->hide();
    setup_viewports();
    setup_actions();
    update_properties_panel();
    connect_slots();
}

MainWindow::~MainWindow()
{
    delete m_scene;
    delete ui;
}

void MainWindow::setup_actions(){
    ui->mainToolBar->addAction("Sphere",this,SLOT(add_object()));
    ui->mainToolBar->addAction("Box",this,SLOT(add_object()));
    ui->mainToolBar->addAction("Octree",this,SLOT(add_object()));
}

void MainWindow::setup_viewports(){
    //TODO: Find a better way to implement multiple viewports, this way sucks.
    GLWidget *viewport1 = new GLWidget(FRONT_VIEW);
    GLWidget *viewport2 = new GLWidget(BACK_VIEW);
    GLWidget *viewport3 = new GLWidget(TOP_VIEW);
    GLWidget *viewport4 = new GLWidget(BOTTOM_VIEW);

    QHBoxLayout *h1 = new QHBoxLayout();
    QHBoxLayout *h2 = new QHBoxLayout();
    QHBoxLayout *h3 = new QHBoxLayout();
    QHBoxLayout *h4 = new QHBoxLayout();

    ui->top_left_viewport_frame->setLayout(h1);
    ui->top_right_viewport_frame->setLayout(h2);
    ui->bottom_left_viewport_frame->setLayout(h3);
    ui->bottom_right_viewport_frame->setLayout(h4);

    ui->top_left_viewport_frame->layout()->addWidget(viewport1);   
    ui->top_right_viewport_frame->layout()->addWidget(viewport2);
    ui->bottom_left_viewport_frame->layout()->addWidget(viewport3);
    ui->bottom_right_viewport_frame->layout()->addWidget(viewport4);

    viewport1->set_scene(m_scene);
    viewport2->set_scene(m_scene);
    viewport3->set_scene(m_scene);
    viewport4->set_scene(m_scene);

    m_viewports.push_back(viewport1);
    m_viewports.push_back(viewport2);
    m_viewports.push_back(viewport3);
    m_viewports.push_back(viewport4);
}

void MainWindow::connect_slots(){
    connect(ui->objects_listWidget,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(objects_list_menu(QPoint)));
    connect(ui->objects_listWidget,SIGNAL(currentRowChanged(int)),this,SLOT(update_transformations_panel()));
    connect(ui->objects_listWidget,SIGNAL(currentRowChanged(int)),this,SLOT(update_properties_panel()));
    connect(ui->subdivide_pushButton,SIGNAL(clicked()),this,SLOT(subdivide()));
    connect(ui->solid_checkBox,SIGNAL(toggled(bool)),this,SLOT(update_obj_properties()));
    connect(ui->wireframe_checkBox,SIGNAL(toggled(bool)),this,SLOT(update_obj_properties()));
    connect(ui->visibile_checkBox,SIGNAL(toggled(bool)),this,SLOT(update_obj_properties()));
    connect(ui->inside_checkBox,SIGNAL(toggled(bool)),this,SLOT(update_obj_properties()));
    connect(ui->outside_checkBox,SIGNAL(toggled(bool)),this,SLOT(update_obj_properties()));
    connect(ui->partially_checkBox,SIGNAL(toggled(bool)),this,SLOT(update_obj_properties()));
//    connect(ui->rot_x_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->rot_y_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->rot_z_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->rot_x_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->tr_x_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->tr_y_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->tr_z_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->sc_x_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->sc_y_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
//    connect(ui->sc_z_doubleSpinBox,SIGNAL(valueChanged(double)),this,SLOT(update_transformations()));
    connect(ui->transform_pushButton,SIGNAL(clicked()),this,SLOT(update_transformations()));
}

void MainWindow::add_object(){
    QAction *action = (QAction*)QObject::sender();
    QListWidgetItem *list_item = NULL;
    if(action->text() == "Sphere"){
        list_item = new QListWidgetItem("Sphere",0,0);
        Object *obj = new Object();
        obj->set_primitive(new Sphere(glm::vec4(0.0,0.0,0.0, 1.0),1.0));
        m_scene->add_object(obj);
    }
    if(action->text() == "Box"){
        list_item = new QListWidgetItem("Box",0,0);
        Object *obj = new Object();
        obj->set_primitive(new Box(glm::vec4(0.0,0.0,0.0, 1.0),1.0));
        m_scene->add_object(obj);
    }
    if(action->text() == "Octree"){
        list_item = new QListWidgetItem("Octree",0,1);
        Octree *tree = new Octree(glm::vec4(0.0,0.0,0.0, 1.0),2.0,5);
        m_scene->add_octree(tree);
    }
    if(list_item != NULL){
        ui->objects_listWidget->addItem(list_item);
    }   
    update_viewports();
}

void MainWindow::subdivide(){
    Octree *tree = m_scene->get_octree(m_selected_object);
    int depth = ui->depth_spinBox->value();
    tree->subdivide(m_scene, depth);
    update_viewports();
}

void MainWindow::update_selected_indexes(int type){
    if(type == 0){
        int num = -1;
        for(int i = 0; i < ui->objects_listWidget->count(); i++){
            if(ui->objects_listWidget->item(i)->type() == 0){
                num++;
            }
            if(i == m_list_position){
                break;
            }
        }
        m_selected_object = num;
        m_selected_type = type;
    }
    if(type == 1){
        int num = -1;
        for(int i = 0; i < ui->objects_listWidget->count(); i++){
            if(ui->objects_listWidget->item(i)->type() == 1){
                num++;
            }
            if(i == m_list_position){
                break;
            }
        }
        m_selected_object = num;
        m_selected_type = type;
    }
}

void MainWindow::objects_list_menu(const QPoint & pos){
    QPoint globalPos =  ui->objects_listWidget->mapToGlobal(pos);	// Map the global position to the userlist
    QModelIndex t =  ui->objects_listWidget->indexAt(pos);
    int list_pos = t.row();
    if(list_pos == -1){
        return;
    }
    ui->objects_listWidget->item(list_pos)->setSelected(true);			// even a right click will select the item
    QMenu *menu = new QMenu();
    Object *obj;
    Octree *tree;

    int type = ui->objects_listWidget->item(list_pos)->type();
    bool visible = false;
    m_list_position = list_pos;
    update_selected_indexes(type);

    if(type == 0){
        obj = m_scene->get_object(m_selected_object);
        visible = obj->is_visible();
    }
    if(type == 1){
        tree = m_scene->get_octree(m_selected_object);
        visible = tree->is_visible();
    }

    if(visible){
        menu->addAction("Hide",this,SLOT(hide_object()));
    }else{
        menu->addAction("Show",this,SLOT(show_object()));
    }
    if(type == 1){
        menu->addAction("Subdivide",this,SLOT(subdivide()));
    }
    menu->addAction("Delete",this,SLOT(delete_object()));
    menu->exec(globalPos);
}

void MainWindow::hide_object(){
    if(m_selected_type == 0){
        Object *obj = m_scene->get_object(m_selected_object);
        obj->set_visible(false);
    }
    if(m_selected_type == 1){
        Octree *octree = m_scene->get_octree(m_selected_object);
        octree->set_visible(false);
    }
    update_viewports();

}

void MainWindow::show_object(){
    if(m_selected_type == 0){
        Object *obj = m_scene->get_object(m_selected_object);
        obj->set_visible(true);
        update_viewports();
    }
    if(m_selected_type == 1){
        Octree *octree = m_scene->get_octree(m_selected_object);
        octree->set_visible(true);
        update_viewports();
    }
}

void MainWindow::update_viewports(){
    for(int i = 0; i < m_viewports.size(); i++){
        m_viewports[i]->updateGL();
    }
}

void MainWindow::delete_object(){
    m_list_position = ui->objects_listWidget->currentRow();
    if(m_list_position < 0){
        return;
    }
    int type = ui->objects_listWidget->item(m_list_position)->type();
    update_selected_indexes(type);
    if(m_selected_type == 0){
        m_scene->delete_object(m_selected_object);
    }
    if(m_selected_type == 1){
        m_scene->delete_octree(m_selected_object);

    }
    update_viewports();
    QListWidgetItem *item = ui->objects_listWidget->item(m_list_position);
    delete item;
}


void MainWindow::update_properties_panel(){
    m_list_position = ui->objects_listWidget->currentRow();
    int type;
    Object *obj;
    Octree *tree;
    if(m_list_position >= 0){
        ui->properties_frame->show();
        type = ui->objects_listWidget->item(m_list_position)->type();
        update_selected_indexes(type);
        if(type == 0){
            ui->nodes_visibility_frame->hide();
            obj = m_scene->get_object(m_selected_object);
            if(obj == NULL){
                return;
            }
            ui->visibile_checkBox->setChecked(obj->is_visible());
            ui->wireframe_checkBox->setChecked(obj->wireframe());
            ui->solid_checkBox->setChecked(obj->solid());
        }
        if(type == 1){
            ui->nodes_visibility_frame->show();
            tree = m_scene->get_octree(m_selected_object);
            if(tree == NULL){
                return;
            }
            ui->visibile_checkBox->setChecked(tree->is_visible());
            ui->wireframe_checkBox->setChecked(tree->wireframe());
            ui->solid_checkBox->setChecked(tree->solid());
            ui->outside_checkBox->setChecked(tree->get_nodes_visibility(Outside));
            ui->inside_checkBox->setChecked(tree->get_nodes_visibility(Inside));
            ui->partially_checkBox->setChecked(tree->get_nodes_visibility(Partially));
        }
    }else{
        ui->properties_frame->hide();
    }
}

void MainWindow::update_obj_properties(){
    bool visible = ui->visibile_checkBox->isChecked();
    bool solid = ui->solid_checkBox->isChecked();
    bool wireframe = ui->wireframe_checkBox->isChecked();
    bool show_in = ui->inside_checkBox->isChecked();
    bool show_out = ui->outside_checkBox->isChecked();
    bool show_part = ui->partially_checkBox->isChecked();

    m_list_position = ui->objects_listWidget->currentRow();
    if(m_list_position >= 0){
        m_selected_type = ui->objects_listWidget->item(m_list_position)->type();
    }else{
        return;
    }
    update_selected_indexes(m_selected_type);

    if(m_selected_type == 0){
        Object *obj = m_scene->get_object(m_selected_object);
        obj->set_visible(visible);
        obj->set_wireframe(wireframe);
        obj->set_solid(solid);
    }
    if(m_selected_type == 1){
        Octree *octree = m_scene->get_octree(m_selected_object);
        octree->set_visible(visible);
        octree->set_wireframe(wireframe);
        octree->set_solid(solid);
        octree->set_nodes_visibility(Inside,show_in);
        octree->set_nodes_visibility(Outside,show_out);
        octree->set_nodes_visibility(Partially,show_part);
    }
    update_viewports();
}

void MainWindow::update_transformations(){    
    float x_rot, y_rot, z_rot;
    float x_tr, y_tr, z_tr;
    float x_sc, y_sc, z_sc;

    x_rot = ui->rot_x_doubleSpinBox->value();
    y_rot = ui->rot_y_doubleSpinBox->value();
    z_rot = ui->rot_z_doubleSpinBox->value();

    x_tr = ui->tr_x_doubleSpinBox->value();
    y_tr = ui->tr_y_doubleSpinBox->value();
    z_tr = ui->tr_z_doubleSpinBox->value();

    x_sc = ui->sc_x_doubleSpinBox->value();
    y_sc = ui->sc_y_doubleSpinBox->value();
    z_sc = ui->sc_z_doubleSpinBox->value();

    m_list_position = ui->objects_listWidget->currentRow();
    if(m_list_position >= 0){
        m_selected_type = ui->objects_listWidget->item(m_list_position)->type();
    }else{
        return;
    }
    update_selected_indexes(m_selected_type);

    if(m_selected_type == 0){
        Object *obj = m_scene->get_object(m_selected_object);
        obj->m_primitive->m_frame->load_identity();
        obj->m_primitive->m_frame->translate(x_tr,y_tr,z_tr);
        obj->m_primitive->m_frame->scale(x_sc,y_sc,z_sc);
        obj->m_primitive->m_frame->rotate(x_rot,glm::vec3(1.0,0.0,0.0));
        obj->m_primitive->m_frame->rotate(y_rot,glm::vec3(0.0,1.0,0.0));
        obj->m_primitive->m_frame->rotate(z_rot,glm::vec3(0.0,0.0,1.0));

        obj->x_rot = x_rot;
        obj->y_rot = y_rot;
        obj->z_rot = z_rot;
        obj->x_tr = x_tr;
        obj->y_tr = y_tr;
        obj->z_tr = z_tr;
        obj->x_sc = x_sc;
        obj->y_sc = y_sc;
        obj->z_sc = z_sc;

    }
    if(m_selected_type == 1){
        Octree *octree = m_scene->get_octree(m_selected_object);
        octree->m_root_frame->load_identity();
        octree->m_root_frame->translate(x_tr,y_tr,z_tr);
        octree->m_root_frame->scale(x_sc,y_sc,z_sc);
        octree->m_root_frame->rotate(x_rot,glm::vec3(1.0,0.0,0.0));
        octree->m_root_frame->rotate(y_rot,glm::vec3(0.0,1.0,0.0));
        octree->m_root_frame->rotate(z_rot,glm::vec3(0.0,0.0,1.0));

        octree->x_rot = x_rot;
        octree->y_rot = y_rot;
        octree->z_rot = z_rot;
        octree->x_tr = x_tr;
        octree->y_tr = y_tr;
        octree->z_tr = z_tr;
        octree->x_sc = x_sc;
        octree->y_sc = y_sc;
        octree->z_sc = z_sc;
    }
    update_viewports();
}

void MainWindow::update_transformations_panel(){
    m_list_position = ui->objects_listWidget->currentRow();
    int type;
    Object *obj;
    Octree *tree;
    if(m_list_position >= 0){
        ui->properties_frame->show();
        type = ui->objects_listWidget->item(m_list_position)->type();
        update_selected_indexes(type);
        if(type == 0){
            obj = m_scene->get_object(m_selected_object);
            if(obj == NULL){
                return;
            }
            ui->rot_x_doubleSpinBox->setValue(obj->x_rot);
            ui->rot_y_doubleSpinBox->setValue(obj->y_rot);
            ui->rot_z_doubleSpinBox->setValue(obj->z_rot);

            ui->tr_x_doubleSpinBox->setValue(obj->x_tr);
            ui->tr_y_doubleSpinBox->setValue(obj->y_tr);
            ui->tr_z_doubleSpinBox->setValue(obj->z_tr);

            ui->sc_x_doubleSpinBox->setValue(obj->x_sc);
            ui->sc_y_doubleSpinBox->setValue(obj->y_sc);
            ui->sc_z_doubleSpinBox->setValue(obj->z_sc);
        }
        if(type == 1){
            tree = m_scene->get_octree(m_selected_object);
            if(tree == NULL){
                return;
            }
            ui->rot_x_doubleSpinBox->setValue(tree->x_rot);
            ui->rot_y_doubleSpinBox->setValue(tree->y_rot);
            ui->rot_z_doubleSpinBox->setValue(tree->z_rot);

            ui->tr_x_doubleSpinBox->setValue(tree->x_tr);
            ui->tr_y_doubleSpinBox->setValue(tree->y_tr);
            ui->tr_z_doubleSpinBox->setValue(tree->z_tr);

            ui->sc_x_doubleSpinBox->setValue(tree->x_sc);
            ui->sc_y_doubleSpinBox->setValue(tree->y_sc);
            ui->sc_z_doubleSpinBox->setValue(tree->z_sc);
        }
    }
}
